/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

const { reject } = require("lodash");

// 1、首先Promise是要有三种状态，pending，fulfilled，rejected
const PENDING = pending;
const FULFILLED = fulfilled;
const REJECTED = rejected;
//然后Promise需要resolve和reject、then
class Promise {
    status = PENDING;
    resolve = value =>{};
    reject = value =>{};
    then (){

    }
}
//Promise需要在构造后传入一个可执行代码,来执行resolve或者reject(这时如果有异常，应该捕获，异常相当于调用reject)
class Promise {
    constructor(exctor){
        try {
            exctor(this.resolve,this.reject)
        } catch (error) {
            this.reject(error)
        }
        
    }
    status = PENDING;
    resolve = value =>{};
    reject = value =>{};
    then (){

    }
}

//之后执行代码后需要根据状态  then调用不同的函数  注意状态是不可逆的回调应该是数组
class Promise {
    constructor(exctor){
        try {
            exctor(this.resolve,this.reject)
        } catch (error) {
            this.reject(error)
        }
        
    }
    //成功代码
    successCallBack = [];
    //失败代码
    faildCallBack = [];
    //resolve和reject的值是需要保存并且在then中使用的
    fulfilled = undefined;

    rejected = undefined;

    status = PENDING;
    //resolve和reject需要改变状态，且状态是不可逆转的,
    // resolve和rejected主要是做三件事
    // 1）修改promise的状态，保存传入的值，处理异步请求的回调函数
    resolve = fulfilled =>{
        if(this.status != PENDING) return;
        this.status = FULFILLED;
        this.fulfilled = fulfilled;
        while(this.successCallBack.length) this.successCallBack.shift()();
    };
    reject = rejected =>{
        if(this.status != PENDING) return;
        this.status = REJECTED;
        this.rejected = rejected;
        while(this.faildCallBack.length) this.faildCallBack.shift()();
    };
    then (successCallBack,faildCallBack){
        //处理then方法的可选参数调用问题，如果有参数则用，没有，补一个
        successCallBack = successCallBack ? successCallBack : value => value;
        faildCallBack = faildCallBack ? faildCallBack : reason => { throw reason }
        //表示成功,调用成功函数。失败调用失败函数  解决异步问题就是加一个setTimeout定时器,链式调用需要返回一个promise对象
        //因为promis的返回值可以是promise对象  也可以是一个常量  所以  在成功和失败回调之后要进行判断对象还是常量
        let promise2 = new Promise((resolve,reject)=>{
                if(this.status == FULFILLED){
                    setTimeout(() =>{
                        try {
                            let x = successCallBack(this.fulfilled)
                            //判断x的值是promise还是普通值
                            //如果是普通值 直接调用reslove
                            //如果是promise对象  查看promise对象返回的结果
                            // 在根据promise返回的结果  决定是调用resolve还是reject
                            resolvePromise(promise2,x,resolve,reject)
                        } catch (error) {
                            reject(error)
                        }
                        
                    },0)
                    
                //表示失败
                }else if(this.status == REJECTED){
                    setTimeout(() => {
                        try {
                            let x = faildCallBack(this.rejected)
                            resolvePromise(promise2,x,resolve,reject)
                        } catch (error) {
                            reject(error)
                        }
                        
                    },0)
                    
                } else {

                    this.successCallBack.push(() => {
                        setTimeout(() => {
                            try {
                            let x = successCallback(this.fulfilled);
                            // 判断 x 的值是普通值还是promise对象
                            // 如果是普通值 直接调用resolve 
                            // 如果是promise对象 查看promsie对象返回的结果 
                            // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
                            resolvePromise(promsie2, x, resolve, reject)
                            }catch (e) {
                            reject(e);
                            }
                        }, 0)
                    });
                    this.faildCallBack.push(() => {
                        setTimeout(() => {
                            try {
                                let x = faildCallBack(this.rejected)
                                resolvePromise(promise2,x,resolve,reject)
                            } catch (error) {
                                reject(error)
                            }
                            
                        },0)
                    });
        
                }
            }
            
        )
        return promise2;
    }

    //finally方法的代码需要执行，所以在成功和失败都调用了回调函数callBack，又因为promise存在异步的问题  then需要进行等待后执行 所以利用resolve
    //将 callBack的代码放到promise中执行
    finally (callBack){
        this.then(value => {
            return promise.resolve(callBack()).then(() => value)
        },reason => {
            return promise.resolve(callBack()).then(() => {throw reason})
        })

    }

    //不注册成功回调，只是注册失败回调就可以模拟catch方法了
    catch (failCallback){
        return this.then(undefined,failCallback)
    }

    static all (array){
        //因为all接收的是一个数组，所以需要定义一个数组，又因为all后面可以跟then方法 所以new 一个promise方法，循环遍历传入all的值，如果是
        //普通值直接放到数组中，如果是promise对象的话，就执行该对象，把结果放到数组中，因为promise中有可能存在异步代码  所以需要一个计数器来
        //进行标记，当全部执行完毕后在进行resolve
        let result = [];
        let index = 0;
        return new promise((resolve,reject) =>{
            function addData (key,value){
                result[key] = value;
                index ++;
                if(index === array.length){
                    resolve(result)
                }
            }
            for(let i = 0; i < array.length ; i ++){
                let current = array[i];
                // 证明是promise对象需要进行执行操作，反之则直接放到数组中
                if(current instanceof promise){
                    current.then(value => addData(i,value),reason => reject(reason))

                }else {
                    addData(i,array[i])
                }

            }

        })

    }

    static resolve (value){
        if(value instanceof promise){
            return value
        }else{
            return new promise((resolve,reject) =>{
                resolve(value)
            })
        }

    }
}

// 判断返回的值是promise还是常量数据，如果是普通数据则直接调用resolve方法  如果是promise则先调用其then方法
function resolvePromise (promise2,x,resolve,reject){
    // 判断当出现自己调用自己的情况时抛错
    if(promise2 === x){
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }

    if(x instanceof promise){
        x.then(value => resolve(value),reson => reject(reson))

    }else{
        resolve(x);
    }
}